home *** CD-ROM | disk | FTP | other *** search
/ Amiga Plus Extra 1997 #2 / Amiga Plus Extra 1997 #2.iso / pd / misc / envwrd41 / source / warpspell / funcs.c < prev    next >
Encoding:
C/C++ Source or Header  |  1997-02-16  |  30.2 KB  |  1,274 lines

  1. /* -----------------------------------------------------------------------------
  2.  
  3.  WarpSPELL ©1996 Dietmar Eilert
  4.  
  5.  Spell checking syntax parser. Dice:
  6.  
  7.  DMAKE
  8.  
  9.  -------------------------------------------------------------------------------
  10.  
  11. */
  12.  
  13. #include "defs.h"
  14.  
  15. /// "Header stuff"
  16.  
  17. // Buffer handles are allocated for each text buffer to keep track of ressources:
  18.  
  19. struct BufferHandle {
  20.  
  21.     struct EditConfig     *bh_EditConfig;            // pointer to text data
  22.     struct GlobalConfig   *bh_GlobalConfig;          // editor configuration
  23.     struct SyntaxChunk    *bh_SyntaxStack;           // parser output
  24.     struct RefreshRequest  bh_RefreshRequest;        // display refresh request
  25.     UBYTE                  bh_Command[4096];         // ISpell command buffer
  26.     UBYTE                  bh_Results[4096];         // ISpell result buffer
  27.     struct MsgPort        *bh_Port;                  // ISpell reply port
  28. };
  29.  
  30. // AutoConfig semaphore coordinates ISpell usage
  31.  
  32. struct AutoConfig {
  33.  
  34.     struct SignalSemaphore Semaphore;
  35.     UWORD                  Users;                    // ISpell users
  36.     ULONG                  Words;                    // new words counter
  37. };
  38.  
  39. #define EMPTY_STACK ((struct SyntaxChunk *)~0)       // empty stack flag
  40.  
  41. ///
  42. /// "Prototype"
  43.  
  44. // library functions
  45.  
  46. Prototype LibCall struct ParserData     *MountScanner(void);
  47. Prototype LibCall ULONG                  StartScanner(__A0 struct GlobalConfig *, __A1 struct EditConfig *, __D0 struct SyntaxChunk *);
  48. Prototype LibCall ULONG                  CloseScanner(__D0 ULONG);
  49. Prototype LibCall void                   FlushScanner(__D0 ULONG);
  50. Prototype LibCall void                   SetupScanner(__A0 struct GlobalConfig  *);
  51. Prototype LibCall struct RefreshRequest *BriefScanner(__D0 ULONG, __A0 struct ScannerNotify *);
  52. Prototype LibCall struct SyntaxChunk    *ParseLine   (__D0 ULONG, __A0 struct LineNode *, __D1 ULONG);
  53. Prototype LibCall void                   UnparseLines(__A0 struct LineNode *, __D0 ULONG);
  54. Prototype LibCall void                   ParseSection(__D0 ULONG, __A0 struct LineNode *, __D1 ULONG);
  55.  
  56. // private functions
  57.  
  58. Prototype struct SyntaxChunk            *ParseString(UBYTE *, UWORD, ULONG);
  59. Prototype struct SyntaxChunk            *DupStack(struct SyntaxChunk *);
  60. Prototype ULONG                         *SendRexxCommand(UBYTE *, UBYTE *, struct MsgPort *replyPort, UBYTE *);
  61. Prototype BOOL                           CheckThisWord(ULONG, UBYTE *, UWORD);
  62. Prototype void                           AlertInfo(UBYTE *);
  63. Prototype struct Window                 *OpenMessageWin(UBYTE *, BOOL);
  64. Prototype struct Window                 *Message(UBYTE *, BOOL, struct Window *);
  65. Prototype struct MsgPort                *RunISpell(void);
  66. Prototype BOOL                           ExistPath(UBYTE *);
  67. Prototype BOOL                           DoExecute(UBYTE *, BOOL, UBYTE *, UBYTE *, ULONG, WORD);
  68. Prototype struct MsgPort *               RunISpell(void);
  69. Prototype struct MsgPort                *WaitForISpell(void);
  70. Prototype void                           StopISpell(void);
  71. Prototype struct FileInfoBlock          *FileInfo(UBYTE *, struct FileInfoBlock *);
  72.  
  73. ///
  74. /// "Globals"
  75.  
  76. struct SignalSemaphore ISpellSemaphore;
  77. BOOL                   IsLetter  [256];
  78. BOOL                   IsBoundary[256];
  79. BOOL                   QuickCheck;
  80.  
  81. ///
  82. /// "Library functions"
  83.  
  84. /* ------------------------------- MountScanner --------------------------------
  85.  
  86.  Called by the editor before first usage of a scanner. Return a description of
  87.  our abilities.
  88.  
  89. */
  90.  
  91. __geta4 LibCall struct ParserData *
  92. MountScanner()
  93. {
  94.     static UBYTE version[] = "$VER: WarpSPELL 2.5 (" __COMMODORE_DATE__ ")";
  95.  
  96.     static struct ParserData parserData;
  97.  
  98.     // syntax elements understood by parser
  99.  
  100.     static UBYTE *levelNames[] = { "Standard text", "Spelling error", NULL };
  101.  
  102.     static UBYTE *example[] = {
  103.  
  104.         "Aaron      ",
  105.         "aback      ",
  106.         "abandon    ",
  107.         "abase      ",
  108.         "abash      ",
  109.         "abate      ",
  110.         "abatement  ",
  111.         "abbe       ",
  112.         "abbess     ",
  113.         "abbey      ",
  114.         "abbot      ",
  115.         "abbreviate ",
  116.         "abdicate   ",
  117.         "abdominal  ",
  118.         "abduct     ",
  119.         "adenoids   ",
  120.         "already    ",
  121.         "alsatian   ",
  122.         "also       ",
  123.         "altar      ",
  124.         "alter      ",
  125.  
  126.         NULL
  127.     };
  128.  
  129.     // color suggestions
  130.  
  131.     static ULONG levelColors[] = {
  132.  
  133.         MAKE_RGB4(0,  0,  0),
  134.         MAKE_RGB4(15, 15, 0),
  135.     };
  136.  
  137.     parserData.pd_Release  = SCANLIBVERSION;
  138.     parserData.pd_Version  = 1;
  139.     parserData.pd_Serial   = 0;
  140.     parserData.pd_Info     = "WarpSPELL 2.5";
  141.     parserData.pd_Levels   = 2;
  142.     parserData.pd_Names    = levelNames;
  143.     parserData.pd_Colors   = levelColors;
  144.     parserData.pd_Flags    = SCPRF_SYNTAXCACHE;
  145.     parserData.pd_Example  = example;
  146.  
  147.     return(&parserData);
  148. }
  149.  
  150.  
  151. /* ------------------------------- StartScanner --------------------------------
  152.  
  153.  Called by the editor after a new text buffer has been created. We allocate a
  154.  buffer to hold text-specific data. The buffer address is returned as handle.
  155.  
  156. */
  157.  
  158. LibCall ULONG
  159. StartScanner(__A0 struct GlobalConfig *globalConfigPtr, __A1 struct EditConfig *editConfigPtr, __D0 struct SyntaxChunk *syntaxStack)
  160. {
  161.     struct BufferHandle *handle;
  162.  
  163.     if (handle = AllocVec(sizeof(struct BufferHandle), MEMF_PUBLIC | MEMF_CLEAR)) {
  164.  
  165.         if (handle->bh_Port = CreateMsgPort()) {
  166.  
  167.             handle->bh_GlobalConfig = globalConfigPtr;
  168.             handle->bh_EditConfig   = editConfigPtr;
  169.             handle->bh_SyntaxStack  = syntaxStack;
  170.  
  171.             if (RunISpell())
  172.  
  173.                 return((ULONG)handle);
  174.  
  175.             else {
  176.  
  177.                 DeleteMsgPort(handle->bh_Port);
  178.  
  179.                 FreeVec(handle);
  180.             }
  181.         }
  182.         else
  183.             FreeVec(handle);
  184.     }
  185.  
  186.     return(NULL);
  187. }
  188.  
  189.  
  190. /* ------------------------------- CloseScanner --------------------------------
  191.  
  192.  Called by the editor if a text buffer is about to be closed. Deallocate buffer
  193.  specific 'global' data.
  194.  
  195. */
  196.  
  197. LibCall ULONG
  198. CloseScanner(__D0 ULONG scanID)
  199. {
  200.     if (scanID) {
  201.  
  202.         struct BufferHandle *handle = (struct BufferHandle *)scanID;
  203.  
  204.         if (handle->bh_Port)
  205.  
  206.             DeleteMsgPort(handle->bh_Port);
  207.  
  208.         StopISpell();
  209.  
  210.         FreeVec(handle);
  211.     }
  212.  
  213.     return(0);
  214. }
  215.  
  216.  
  217. /* ------------------------------- FlushScanner --------------------------------
  218.  
  219.  Called by the editor in low memory situations: we are supposed to free as much
  220.  memory as possible.
  221.  
  222. */
  223.  
  224. LibCall void
  225. FlushScanner(__D0 ULONG scanID)
  226. {
  227.     struct BufferHandle *handle;
  228.     struct EditConfig   *config;
  229.     struct LineNode     *lineNode;
  230.  
  231.     handle = (struct BufferHandle *)scanID;
  232.     config = handle->bh_EditConfig;
  233.  
  234.     if (lineNode = config->TextNodes)
  235.  
  236.         UnparseLines(lineNode, config->Lines);
  237. }
  238.  
  239.  
  240. /* ------------------------------- SetupScanner --------------------------------
  241.  
  242.  Called by the editor if the user wants to change the scanner's configuration.
  243.  We do not support user configuration (parserData.pd_Flags: SCPRF_CONFIGWIN flag
  244.  unset).
  245.  
  246. */
  247.  
  248. LibCall void
  249. SetupScanner(__A0 globalConfigPtr)
  250. {
  251.     ;
  252. }
  253.  
  254.  
  255. /* ------------------------------- BriefScanner --------------------------------
  256.  
  257.  Called to notify a context scanner if lines have been added, deleted or
  258.  modified. We aren't a context scanner (parserData.pd_Flags: SCPRF_CONTEXT
  259.  flag unset), so we won't ever have to request additional display requests:
  260.  the editor's built-in refresh of damage regions is sufficient.
  261.  
  262. */
  263.  
  264. LibCall struct RefreshRequest *
  265. BriefScanner(__D0 ULONG scanID, __A0 struct ScannerNotify *notify)
  266. {
  267.     return(NULL);
  268. }
  269.  
  270.  
  271. /* --------------------------------- ParseLine ---------------------------------
  272.  
  273.  Parse a line, build a syntax description
  274.  
  275. */
  276.  
  277. LibCall struct SyntaxChunk *
  278. ParseLine(__D0 ULONG scanID, __A0 struct LineNode *lineNode, __D1 ULONG line)
  279. {
  280.     if (IS_FOLD(lineNode))
  281.  
  282.         return(NULL);
  283.  
  284.     else if (lineNode->Len) {
  285.  
  286.         // line not yet parsed ?
  287.  
  288.         if (lineNode->UserData == NULL) {
  289.  
  290.             struct SyntaxChunk *syntaxStack = ParseString(lineNode->Text, lineNode->Len, scanID);
  291.  
  292.             if (syntaxStack == EMPTY_STACK)
  293.                 lineNode->UserData = EMPTY_STACK;
  294.             else
  295.                 lineNode->UserData = DupStack(syntaxStack);
  296.         }
  297.  
  298.         if (lineNode->UserData == EMPTY_STACK)
  299.             return((struct SyntaxChunk *)NULL);
  300.         else
  301.             return((struct SyntaxChunk *)lineNode->UserData);
  302.     }
  303.     else
  304.         return(NULL);
  305. }
  306.  
  307.  
  308. /* -------------------------------- UnparseLines -------------------------------
  309.  
  310.  Called by the editor if lines are to be deleted. We are supposed to free
  311.  private data attached to the lines.
  312.  
  313. */
  314.  
  315. LibCall void
  316. UnparseLines(__A0  struct LineNode *lineNode, __D0 ULONG lines)
  317. {
  318.     while (lines--) {
  319.  
  320.         // free syntax cache
  321.  
  322.         if (lineNode->UserData) {
  323.  
  324.             if (lineNode->UserData != (APTR)EMPTY_STACK)
  325.                 FreeVec((APTR)lineNode->UserData);
  326.  
  327.             lineNode->UserData = NULL;
  328.         }
  329.  
  330.         // free folded subblock
  331.  
  332.         if (IS_FOLD(lineNode)) {
  333.  
  334.             struct Fold *fold = (struct Fold *)lineNode->SpecialInfo;
  335.  
  336.             UnparseLines(fold->TextNodes, fold->Lines);
  337.         }
  338.  
  339.         ++lineNode;
  340.     }
  341. }
  342.  
  343.  
  344. /* -------------------------------- ParseSection -------------------------------
  345.  
  346.  Called by the editor if lines are to be displayed. The scanner is encouraged to
  347.  preparse the lines.
  348.  
  349. */
  350.  
  351. LibCall void
  352. ParseSection(__D0 ULONG scanID, __A0  struct LineNode *lineNode, __D1 ULONG lines)
  353. {
  354.     struct Window *win;
  355.     ULONG          done;
  356.     UBYTE          title[80];
  357.  
  358.     if (lines > 100)
  359.         win = Message("Checking text...", FALSE, NULL);
  360.     else
  361.         win = NULL;
  362.  
  363.     for (done = 0; done < lines; ++done) {
  364.  
  365.         if (win) {
  366.  
  367.             if ((done & 63) == 63) {
  368.  
  369.                 sprintf(title, "Words (%d/%d)", done, lines);
  370.  
  371.                 SetWindowTitles(win, title, (UBYTE *)~0);
  372.             }
  373.         }
  374.  
  375.         // fold headers have to be ignored
  376.  
  377.         if (IS_FOLD(lineNode) == FALSE) {
  378.  
  379.             // line not yet parsed ?
  380.  
  381.             if (lineNode->Len)
  382.  
  383.                 if (lineNode->UserData == NULL)
  384.  
  385.                     lineNode->UserData = DupStack(ParseString(lineNode->Text, lineNode->Len, scanID));
  386.         }
  387.  
  388.         ++lineNode;
  389.     }
  390.  
  391.     if (win)
  392.         CloseWindow(win);
  393. }
  394.  
  395. ///
  396. /// "Private"
  397.  
  398. /* -------------------------------- ParseString --------------------------------
  399.  
  400.  Parse a string, build a syntax description. This is a simple example only:
  401.  C++-Comments (// ....) are highlighted. Return EMPTY_STACK in case there is
  402.  nothing to highlight.
  403.  
  404. */
  405.  
  406. struct SyntaxChunk *
  407. ParseString(UBYTE *text, UWORD len, ULONG scanID)
  408. {
  409.     if (len) {
  410.  
  411.         struct SyntaxChunk *syntaxStack = ((struct BufferHandle *)scanID)->bh_SyntaxStack;
  412.  
  413.         UWORD indent, elements, wordLen;
  414.  
  415.         elements = 0;
  416.  
  417.         // leading spaces have to be ignored
  418.  
  419.         for (indent = 0; len && (*text == 32); ++indent, --len)
  420.  
  421.             ++text;
  422.  
  423.         // trailing spaces have to be ignored
  424.  
  425.         while (len && (text[len - 1] == 32))
  426.  
  427.             --len;
  428.  
  429.         // check every word of this line
  430.  
  431.         while (len) {
  432.  
  433.             // ignore spaces
  434.  
  435.             while (len && (IsLetter[*text] == FALSE)) {
  436.  
  437.                 ++text;
  438.                 ++indent;
  439.                 --len;
  440.             }
  441.  
  442.             if (len) {
  443.  
  444.                 // get next word
  445.  
  446.                 for (wordLen = 1; wordLen < len; ++wordLen) {
  447.  
  448.                     UBYTE *check = text + wordLen;
  449.  
  450.                     // end of word detected ?
  451.  
  452.                     if (IsLetter[*check] == FALSE) {
  453.  
  454.                         // verify end of word (boundary characters can be space OR letter)
  455.  
  456.                         if (IsBoundary[*check]) {
  457.  
  458.                             if ((wordLen + 1) < len) {
  459.  
  460.                                 if (IsLetter[*++check] == FALSE)
  461.  
  462.                                     break;
  463.                             }
  464.                             else
  465.                                 break;
  466.                         }
  467.                         else
  468.                             break;
  469.                     }
  470.                 }
  471.  
  472.                 // check word
  473.  
  474.                 if (CheckThisWord(scanID, text, wordLen)) {
  475.  
  476.                     // highlight word
  477.  
  478.                     syntaxStack[elements].sc_Start = indent;
  479.                     syntaxStack[elements].sc_End   = indent + (wordLen - 1);
  480.                     syntaxStack[elements].sc_Level = 1;
  481.  
  482.                     ++elements;
  483.                 }
  484.  
  485.                 // move to next word
  486.  
  487.                 text   += wordLen;
  488.                 indent += wordLen;
  489.                 len    -= wordLen;
  490.             }
  491.         }
  492.  
  493.         if (elements) {
  494.  
  495.             // terminate syntax stack
  496.  
  497.             syntaxStack[elements].sc_Start = FALSE;
  498.             syntaxStack[elements].sc_End   = FALSE;
  499.             syntaxStack[elements].sc_Level = FALSE;
  500.  
  501.             return(syntaxStack);
  502.  
  503.         }
  504.     }
  505.  
  506.     return(EMPTY_STACK);
  507. }
  508.  
  509. /* --------------------------------- DupStack ----------------------------------
  510.  
  511.  Duplicate syntax stack (to be FreeVec'ed). Return NULL in case of failure.
  512.  
  513. */
  514.  
  515. struct SyntaxChunk *
  516. DupStack(syntaxStack)
  517.  
  518. struct SyntaxChunk *syntaxStack;
  519. {
  520.     if (syntaxStack && (syntaxStack != EMPTY_STACK)) {
  521.  
  522.         struct SyntaxChunk *chunk;
  523.         UWORD               elements;
  524.  
  525.         // determine stack size
  526.  
  527.         for (elements = 0, chunk = syntaxStack; chunk->sc_Level; ++chunk)
  528.  
  529.             ++elements;
  530.  
  531.         // create copy of syntax stack (to be attached to a text line by the caller)
  532.  
  533.         if (elements) {
  534.  
  535.             ULONG size = (++elements) * sizeof(struct SyntaxChunk);
  536.  
  537.             chunk = syntaxStack;
  538.  
  539.             if (syntaxStack = AllocVec(size, MEMF_PUBLIC))
  540.                 movmem(chunk, syntaxStack, size);
  541.         }
  542.         else
  543.             syntaxStack = EMPTY_STACK;
  544.     }
  545.  
  546.     return(syntaxStack);
  547. }
  548.  
  549. /* ------------------------------- CheckThisWord -------------------------------
  550.  
  551.  Check word <check> of length <len>. Return TRUE if word is not known. Ignore
  552.  single letters.
  553.  
  554. */
  555.  
  556. __geta4 BOOL
  557. CheckThisWord(scanID, check, len)
  558.  
  559. UBYTE  *check;
  560. UWORD   len;
  561. ULONG   scanID;
  562. {
  563.     BOOL error = FALSE;
  564.  
  565.     if (len > 1) {
  566.  
  567.         struct BufferHandle *handle = (struct BufferHandle *)scanID;
  568.  
  569.         UBYTE *command = handle->bh_Command;
  570.  
  571.         movmem(check, command, len);
  572.  
  573.         command[len] = 0;
  574.  
  575.         strins(command, "QUICKCHECK ");
  576.  
  577.         // make ISpell check command quickly
  578.  
  579.         if (SendRexxCommand("IRexxSpell", command, handle->bh_Port, handle->bh_Results)) {
  580.  
  581.             // unknown word (not 'ok') ?
  582.  
  583.             if (stricmp(handle->bh_Results, "ok")) {
  584.  
  585.                 if (QuickCheck)
  586.  
  587.                     error = TRUE;
  588.  
  589.                 else if (SendRexxCommand("IRexxSpell", command + 5, handle->bh_Port, handle->bh_Results)) {
  590.  
  591.                     switch (*handle->bh_Results) {
  592.  
  593.                         case '&':                        // spelling error, near misses available
  594.                         case '?':                        // spelling error, no near misses
  595.  
  596.                             error = TRUE;
  597.                             break;
  598.  
  599.                         case '*':                        // valid word
  600.                         case '+':                        // valid after affix removal
  601.                         case '-':                        // valid compound
  602.  
  603.                             error = FALSE;
  604.                             break;
  605.  
  606.                         default:
  607.  
  608.                             error = FALSE;               // unknown return code (assume that word is valid)
  609.                     }
  610.                 }
  611.             }
  612.         }
  613.     }
  614.  
  615.     return(error);
  616. }
  617.  
  618.  
  619. /* ---------------------------------- SendRexxCommand -------------------------
  620.  
  621.  Send ARexx message & wait for answer. Return pointer to result or NULL.
  622.  
  623. */
  624.  
  625. __geta4 ULONG *
  626. SendRexxCommand(port, cmd, replyPort, buffer)
  627.  
  628. struct MsgPort *replyPort;
  629. UBYTE          *cmd, *port, *buffer;
  630. {
  631.     struct MsgPort *rexxport;
  632.  
  633.     Forbid();
  634.  
  635.     if (rexxport = FindPort(port)) {
  636.  
  637.         struct RexxMsg *rexxMsg, *answer;
  638.  
  639.         if (rexxMsg = CreateRexxMsg(replyPort, NULL, NULL)) {
  640.  
  641.             if (rexxMsg->rm_Args[0] = CreateArgstring(cmd, strlen(cmd))) {
  642.  
  643.                 static ULONG result;
  644.  
  645.                 rexxMsg->rm_Action = RXCOMM | RXFF_RESULT;
  646.  
  647.                 PutMsg(rexxport, &rexxMsg->rm_Node);
  648.  
  649.                 do {
  650.  
  651.                     WaitPort(replyPort);
  652.  
  653.                     if (answer = (struct RexxMsg *)GetMsg(replyPort))
  654.  
  655.                         result = answer->rm_Result1;
  656.  
  657.                 } while (!answer);
  658.  
  659.                 Permit();
  660.  
  661.                 if (answer->rm_Result1 == RC_OK) {
  662.  
  663.                     if (answer->rm_Result2) {
  664.  
  665.                         if (buffer)
  666.                             strcpy(buffer, (char *)answer->rm_Result2);
  667.  
  668.                         DeleteArgstring((char *)answer->rm_Result2);
  669.                     }
  670.                 }
  671.  
  672.                 DeleteArgstring((char *)ARG0(answer));
  673.  
  674.                 DeleteRexxMsg(answer);
  675.  
  676.                 return(&result);
  677.             }
  678.         }
  679.     }
  680.  
  681.     Permit();
  682.  
  683.     return(NULL);
  684. }
  685.  
  686. /* --------------------------------- AlertInfo -----------------------------------
  687.  
  688.  Show alert. Returns TRUE if user pressed left button.
  689.  
  690. */
  691.  
  692. __geta4 void
  693. AlertInfo(text)
  694.  
  695. UBYTE *text;
  696. {
  697.     UBYTE *buffer;
  698.  
  699.     if (buffer = AllocVec(80, MEMF_PUBLIC | MEMF_CLEAR)) {
  700.  
  701.         buffer[1] = '\30';
  702.         buffer[2] = '\25';
  703.  
  704.         strcpy(buffer + 3, text);
  705.  
  706.         DisplayAlert(RECOVERY_ALERT, buffer, 40);
  707.  
  708.         FreeVec(buffer);
  709.     }
  710. }
  711.  
  712.  
  713. /* -------------------------------- OpenMessageWin -----------------------------
  714.  
  715.  Open info window
  716.  
  717. */
  718.  
  719. struct Window *
  720. OpenMessageWin(text, sync)
  721.  
  722. UBYTE *text;
  723. BOOL   sync;
  724. {
  725.     struct TextAttr textAttr = { "topaz.font", 8, 0, FPF_DESIGNED };
  726.  
  727.     struct Window   *win;
  728.     struct Screen   *screen;
  729.     struct TextFont *font;
  730.     WORD             len, width, height, fontW, fontH, x, y, displayW, displayH, xOffset, yOffset;
  731.  
  732.     displayW = FALSE;
  733.     displayH = FALSE;
  734.  
  735.     // critical stuff (barely legal - don't do this at home, kids ;-)
  736.  
  737.     Forbid();
  738.  
  739.     if (screen = ((struct IntuitionBase *)IntuitionBase)->ActiveScreen) {
  740.  
  741.         struct Rectangle clip;
  742.  
  743.         if (QueryOverscan(GetVPModeID(&screen->ViewPort), &clip, OSCAN_TEXT)) {
  744.  
  745.             displayW = clip.MaxX - clip.MinX + 1;
  746.             displayH = clip.MaxY - clip.MinY + 1;
  747.         }
  748.  
  749.         xOffset = -screen->ViewPort.DxOffset;
  750.         yOffset = -screen->ViewPort.DyOffset;
  751.  
  752.         textAttr = *screen->Font;
  753.     }
  754.  
  755.     Permit();
  756.  
  757.     if (font = OpenDiskFont(&textAttr)) {
  758.  
  759.         fontW = font->tf_XSize;
  760.         fontH = font->tf_YSize;
  761.  
  762.         CloseFont(font);
  763.     }
  764.     else
  765.         fontW = fontH = textAttr.ta_YSize;
  766.  
  767.     len = strlen(text);
  768.  
  769.     if (len < 30)
  770.         len = 30;
  771.  
  772.     width  = 20 + fontW * len;
  773.     height = 20 + fontH;
  774.  
  775.     if (displayW) {
  776.  
  777.         x = ((displayW - width )>>1) + xOffset;
  778.         y = ((displayH - height)>>1) + yOffset;
  779.     }
  780.     else
  781.         x = y = 0;
  782.  
  783.     win = OpenWindowTags(NULL,
  784.  
  785.         WA_PubScreen,     screen,
  786.         WA_Left,          x,
  787.         WA_Top,           y,
  788.         WA_InnerWidth,    width,
  789.         WA_InnerHeight,   height,
  790.         WA_Title,        "Words",
  791.         WA_ScreenTitle,  "Words",
  792.         WA_Flags,         WFLG_DRAGBAR | WFLG_DEPTHGADGET | WFLG_SMART_REFRESH | WFLG_ACTIVATE | WFLG_RMBTRAP,
  793.         WA_GimmeZeroZero, TRUE,
  794.         TAG_DONE
  795.     );
  796.  
  797.     if (win) {
  798.  
  799.         if (font)
  800.             SetFont(win->RPort, font);
  801.  
  802.         win = Message(text, sync, win);
  803.     }
  804.  
  805.     return(win);
  806. }
  807.  
  808.  
  809. /* ---------------------------------- Message ----------------------------------
  810.  
  811.  Show message (assume that window is sufficiently sized)
  812.  
  813. */
  814.  
  815. struct Window *
  816. Message(text, sync, win)
  817.  
  818. struct Window *win;
  819. UBYTE         *text;
  820. BOOL           sync;
  821. {
  822.     if (win == NULL)
  823.  
  824.         win = OpenMessageWin(text, sync);
  825.  
  826.     else {
  827.  
  828.         struct RastPort *rast = win->RPort;
  829.  
  830.         SetAPen(rast, 0);
  831.         SetRast(rast, 0);
  832.         SetAPen(rast, 1);
  833.  
  834.         Move(rast, 10, 10 + rast->TxBaseline);
  835.  
  836.         Text(rast, text, strlen(text));
  837.  
  838.         if (sync) {
  839.  
  840.             Delay(150);
  841.  
  842.             CloseWindow(win);
  843.  
  844.             win = NULL;
  845.         }
  846.     }
  847.  
  848.     return(win);
  849. }
  850.  
  851.  
  852. /* --------------------------------- ExistPath -----------------------------------
  853.  
  854.  Check wether file/directory exists.
  855.  
  856. */
  857.  
  858. BOOL
  859. ExistPath(path)
  860.  
  861. UBYTE *path;
  862. {
  863.     BPTR lock;
  864.  
  865.     if (lock = Lock(path, ACCESS_READ))
  866.         UnLock(lock);
  867.  
  868.     if (lock)
  869.         return(TRUE);
  870.     else
  871.         return(FALSE);
  872. }
  873.  
  874.  
  875. /* --------------------------------- DoExecute ---------------------------------
  876.  
  877.  Run DOS command
  878.  
  879. */
  880.  
  881. BOOL
  882. DoExecute(cmd, async, output, directory, stack, prio)
  883.  
  884. UBYTE *cmd, *output, *directory;
  885. ULONG  stack;
  886. WORD   prio;
  887. BOOL   async;
  888. {
  889.     BPTR newCurrentDir;
  890.     BOOL success;
  891.  
  892.     success = FALSE;
  893.  
  894.     if (newCurrentDir = Lock(directory, SHARED_LOCK)) {
  895.  
  896.         BPTR inHandle, outHandle, oldCurrentDir;
  897.  
  898.         oldCurrentDir = CurrentDir(newCurrentDir);
  899.  
  900.         if (outHandle = Open(output, MODE_NEWFILE)) {
  901.  
  902.             struct MsgPort *oldct, *newct;
  903.  
  904.             if (IsInteractive(outHandle)) {
  905.  
  906.                 newct = ((struct FileHandle *)BADDR(outHandle))->fh_Type;
  907.  
  908.                 oldct = SetConsoleTask(newct);
  909.  
  910.                 inHandle = Open("CONSOLE:", MODE_OLDFILE);
  911.  
  912.                 SetConsoleTask(oldct);
  913.             }
  914.             else {
  915.  
  916.                 newct = NULL;
  917.  
  918.                 inHandle = Open("NIL:", MODE_OLDFILE);
  919.             }
  920.  
  921.             if (inHandle) {
  922.  
  923.                 struct TagItem tagItems[] = {
  924.  
  925.                     SYS_Output,     (ULONG)NULL,
  926.                     SYS_Input,      (ULONG)NULL,
  927.                     NP_ConsoleTask, (ULONG)NULL,
  928.                     SYS_Asynch,     (ULONG)FALSE,
  929.                     NP_StackSize,   (ULONG)8196,
  930.                     NP_Priority,    (ULONG)0,
  931.                     SYS_UserShell,  (ULONG)TRUE,
  932.                     TAG_DONE
  933.                 };
  934.  
  935.                 tagItems[0].ti_Data = (ULONG)outHandle;
  936.                 tagItems[1].ti_Data = (ULONG)inHandle;
  937.                 tagItems[2].ti_Data = (ULONG)newct;
  938.                 tagItems[3].ti_Data = (ULONG)async;
  939.                 tagItems[4].ti_Data = (ULONG)stack;
  940.                 tagItems[5].ti_Data = (ULONG)prio;
  941.  
  942.                 if (SystemTagList(cmd, tagItems) != -1L)
  943.  
  944.                     success = TRUE;
  945.  
  946.                 if (!(async && success))
  947.  
  948.                     Close(inHandle);
  949.             }
  950.  
  951.             if (!(async && success))
  952.  
  953.                 Close(outHandle);
  954.         }
  955.  
  956.         CurrentDir(oldCurrentDir);
  957.  
  958.         UnLock(newCurrentDir);
  959.     }
  960.  
  961.     return(success);
  962. }
  963.  
  964.  
  965. /* --------------------------------- FileInfo ----------------------------------
  966.  
  967.  Return file information or NULL (writes to <fib>)
  968.  
  969. */
  970.  
  971. struct FileInfoBlock *
  972. FileInfo(name, fib)
  973.  
  974. struct FileInfoBlock *fib;
  975. UBYTE                *name;
  976. {
  977.     BPTR handle;
  978.  
  979.     if (handle = Lock(name, ACCESS_READ)) {
  980.  
  981.         if (Examine(handle, fib)) {
  982.  
  983.             UnLock(handle);
  984.  
  985.             return(fib);
  986.         }
  987.         else
  988.             UnLock(handle);
  989.     }
  990.  
  991.     return(NULL);
  992. }
  993.  
  994. ///
  995. /// "ISpell"
  996.  
  997. /* --------------------------------- RunISpell ---------------------------------
  998.  
  999.  Run ISpell (if not yet running); increase user count
  1000.  
  1001. */
  1002.  
  1003. struct MsgPort *
  1004. RunISpell()
  1005. {
  1006.     struct MsgPort    *port;
  1007.     struct AutoConfig *autoConfig;
  1008.  
  1009.     // serialize access within this library
  1010.  
  1011.     ObtainSemaphore(&ISpellSemaphore);
  1012.  
  1013.     Forbid();
  1014.  
  1015.     // ISpell running/starting already ?
  1016.  
  1017.     if (autoConfig = (struct AutoConfig *)FindSemaphore("WORDS")) {
  1018.  
  1019.         ObtainSemaphore(&autoConfig->Semaphore);
  1020.  
  1021.         ++autoConfig->Users;
  1022.  
  1023.         ReleaseSemaphore(&autoConfig->Semaphore);
  1024.  
  1025.         port = WaitForISpell();
  1026.  
  1027.         if (port == NULL)
  1028.  
  1029.             Message("STARTUP FAILURE", TRUE, NULL);
  1030.     }
  1031.     else {
  1032.  
  1033.         port = FindPort("IRexxSpell");
  1034.  
  1035.         // run ISpell on our own ?
  1036.  
  1037.         if (port == NULL) {
  1038.  
  1039.             struct AutoConfig *autoConfig;
  1040.  
  1041.             if (autoConfig = (struct AutoConfig *)AllocVec(sizeof(struct AutoConfig), MEMF_PUBLIC | MEMF_CLEAR)) {
  1042.  
  1043.                 static UBYTE hashfile[255], language[255], personal[255], options[255], command[255], home[255];
  1044.  
  1045.                 __aligned struct FileInfoBlock fib = { 0 };
  1046.  
  1047.                 autoConfig->Semaphore.ss_Link.ln_Name = "WORDS";
  1048.                 autoConfig->Semaphore.ss_Link.ln_Pri  = 0;
  1049.                 autoConfig->Users                     = 1;
  1050.  
  1051.                 AddSemaphore   (&autoConfig->Semaphore);
  1052.                 ObtainSemaphore(&autoConfig->Semaphore);
  1053.  
  1054.                 // set default language
  1055.  
  1056.                 strcpy(language, "english");
  1057.  
  1058.                 // set default options
  1059.  
  1060.                 strcpy(options,  "-W 1");
  1061.  
  1062.                 // did user provide special startup arguments for ISpell ?
  1063.  
  1064.                 if (GetVar("WORDS.prefs", command, sizeof(command), GVF_GLOBAL_ONLY) != -1) {
  1065.  
  1066.                     if (*command) {
  1067.  
  1068.                         struct RDArgs *rdArgs, *args;
  1069.  
  1070.                         if (rdArgs = AllocDosObject(DOS_RDARGS, NULL)) {
  1071.  
  1072.                             ULONG argArray[] = { 0, 0, 0, 0, 0 };
  1073.  
  1074.                             // make LF-terminated copy of command string (required by dos/readArgs):
  1075.  
  1076.                             strcat(command, "\12");
  1077.  
  1078.                             rdArgs->RDA_Source.CS_Buffer = command;
  1079.                             rdArgs->RDA_Source.CS_Length = strlen(command);
  1080.                             rdArgs->RDA_Source.CS_CurChr = 0;
  1081.                             rdArgs->RDA_DAList           = 0;
  1082.                             rdArgs->RDA_Buffer           = NULL;
  1083.  
  1084.                             if (args = ReadArgs("LANGUAGE/K,QUICKCHECK/K,BOUNDARYCHARS/K,BEEP/N,ARGS/F", argArray, rdArgs)) {
  1085.  
  1086.                                 if (argArray[0])             // LANGUAGE/K
  1087.                                     strcpy(language, (UBYTE *)argArray[0]);
  1088.  
  1089.                                 if (argArray[4])             // ARGS/F
  1090.                                     strcpy(options,  (UBYTE *)argArray[4]);
  1091.  
  1092.                                 FreeArgs(args);
  1093.                             }
  1094.                             else
  1095.                                 Message("Syntax error", TRUE, NULL);
  1096.  
  1097.                             FreeDosObject(DOS_RDARGS, rdArgs);
  1098.                         }
  1099.                     }
  1100.                 }
  1101.  
  1102.                 // personal dictionary file (for new words)
  1103.  
  1104.                 sprintf(personal, "/ispell/lib/.ispell_%s", language);
  1105.  
  1106.                 // standard dictionary file
  1107.  
  1108.                 sprintf(hashfile, "ispell:lib/%s.hash", language);
  1109.  
  1110.                 // dictionary available ?
  1111.  
  1112.                 if (FileInfo(hashfile, &fib)) {
  1113.  
  1114.                     ULONG avail = AvailMem(MEMF_LARGEST);
  1115.  
  1116.                     if (fib.fib_Size < avail) {
  1117.  
  1118.                         struct Window *win = Message("Starting ISpell...", FALSE, NULL);
  1119.  
  1120.                         // $HOME required by ISpell 3.1.18
  1121.  
  1122.                         if (GetVar("HOME", home, sizeof(home), GVF_GLOBAL_ONLY) == -1)
  1123.  
  1124.                             SetVar("HOME", "sys:", -1, GVF_GLOBAL_ONLY);
  1125.  
  1126.                         // run ISpell in host mode
  1127.  
  1128.                         if (*options)
  1129.                             sprintf(command, "ispell:bin/ispell -d%s -p%s -r %s", hashfile, personal, options);
  1130.                         else
  1131.                             sprintf(command, "ispell:bin/ispell -d%s -p%s -r", hashfile, personal);
  1132.  
  1133.                         // wait for completion of ISpell startup
  1134.  
  1135.                         if (DoExecute(command, TRUE, "NIL:", "ispell:", 32768, 1))
  1136.  
  1137.                             port = WaitForISpell();
  1138.  
  1139.                         if (port == NULL)
  1140.  
  1141.                             Message("STARTUP FAILURE", TRUE, win);
  1142.  
  1143.                         else if (win)
  1144.  
  1145.                             CloseWindow(win);
  1146.                     }
  1147.                     else {
  1148.  
  1149.                         if (fib.fib_Size < AvailMem(MEMF_ANY))
  1150.                             Message("Out of RAM (fragmentation error)", TRUE, NULL);
  1151.                         else
  1152.                             Message("Out of RAM", TRUE, NULL);
  1153.                     }
  1154.                 }
  1155.                 else
  1156.                     Message("Dictionary not found", TRUE, NULL);
  1157.  
  1158.                 ReleaseSemaphore(&autoConfig->Semaphore);
  1159.  
  1160.                 if (port == NULL)
  1161.  
  1162.                     StopISpell();
  1163.             }
  1164.         }
  1165.     }
  1166.  
  1167.     Permit();
  1168.  
  1169.     ReleaseSemaphore(&ISpellSemaphore);
  1170.  
  1171.     return(port);
  1172. }
  1173.  
  1174.  
  1175. /* ------------------------------- WaitForISpell -------------------------------
  1176.  
  1177.  Wait for ISpell
  1178.  
  1179. */
  1180.  
  1181. struct MsgPort *
  1182. WaitForISpell()
  1183. {
  1184.     struct MsgPort *port;
  1185.     UWORD           try;
  1186.  
  1187.     for (try = 50; try-- && (port == NULL); Delay(10)) {
  1188.  
  1189.         Forbid();
  1190.  
  1191.         port = FindPort("IRexxSpell");
  1192.  
  1193.         Permit();
  1194.     }
  1195.  
  1196.     return(port);
  1197. }
  1198.  
  1199.  
  1200. /* -------------------------------- StopISpell ---------------------------------
  1201.  
  1202.  Decrement ISpell user count. Unload ISpell if unused.
  1203.  
  1204. */
  1205.  
  1206. void
  1207. StopISpell()
  1208. {
  1209.     struct AutoConfig *autoConfig;
  1210.  
  1211.     Forbid();
  1212.  
  1213.     if (autoConfig = (struct AutoConfig *)FindSemaphore("WORDS")) {
  1214.  
  1215.         ObtainSemaphore(&autoConfig->Semaphore);
  1216.  
  1217.         if (autoConfig->Users)
  1218.  
  1219.             --autoConfig->Users;
  1220.  
  1221.         ReleaseSemaphore(&autoConfig->Semaphore);
  1222.  
  1223.         if (autoConfig->Users == 0) {
  1224.  
  1225.             struct MsgPort *port;
  1226.  
  1227.             RemSemaphore    (&autoConfig->Semaphore);
  1228.             ObtainSemaphore (&autoConfig->Semaphore);
  1229.             ReleaseSemaphore(&autoConfig->Semaphore);
  1230.  
  1231.             FreeVec(autoConfig);
  1232.  
  1233.             if (port = CreateMsgPort()) {
  1234.  
  1235.                 if (autoConfig->Words) {
  1236.  
  1237.                     struct Screen *screen;
  1238.                     ULONG          lock;
  1239.  
  1240.                     // critical stuff (barely legal - don't do this at home, kids ;-)
  1241.  
  1242.                     lock = LockIBase(0);
  1243.  
  1244.                     // find frontmost screen
  1245.  
  1246.                     screen = ((struct IntuitionBase *)IntuitionBase)->ActiveScreen;
  1247.  
  1248.                     // keep system frozen
  1249.  
  1250.                     Forbid();
  1251.  
  1252.                     // enable rendering functions
  1253.  
  1254.                     UnlockIBase(lock);
  1255.  
  1256.                     if (rtEZRequestTags("Save new words to user dictionary ?", "_SAVE|_cancel", NULL, NULL, RT_Underscore, '_', RTEZ_ReqTitle, "Words", RT_Screen, screen, TAG_DONE) == 1)
  1257.  
  1258.                         SendRexxCommand("IRexxSpell", "ADD A", port, NULL);
  1259.  
  1260.                     Permit();
  1261.                 }
  1262.  
  1263.                 SendRexxCommand("IRexxSpell", "EXIT", port, NULL);
  1264.  
  1265.                 DeleteMsgPort(port);
  1266.             }
  1267.         }
  1268.     }
  1269.  
  1270.     Permit();
  1271. }
  1272.  
  1273. ///
  1274.